package com.yami.shop.search.common.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.yami.shop.bean.bo.BrandLangBO;
import com.yami.shop.bean.bo.CategoryLangBO;
import com.yami.shop.bean.enums.ProdStatusEnums;
import com.yami.shop.bean.param.EsProductParam;
import com.yami.shop.bean.vo.search.*;
import com.yami.shop.common.bean.LangConfig;
import com.yami.shop.common.enums.StatusEnum;
import com.yami.shop.common.i18n.I18nMessage;
import com.yami.shop.common.i18n.LanguageEnum;
import com.yami.shop.common.util.EsPageVO;
import com.yami.shop.config.ShopConfig;
import com.yami.shop.manager.impl.LangManager;
import com.yami.shop.search.common.constant.EsConstant;
import com.yami.shop.search.common.constant.EsIndexEnum;
import com.yami.shop.search.common.constant.EsProductSortEnum;
import com.yami.shop.search.common.param.EsPageParam;
import com.yami.shop.search.common.service.SearchMysteryBoxService;
import com.yami.shop.search.common.util.EsSearchUtil;
import com.yami.shop.search.common.util.SearchResponseUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.common.lucene.search.function.CombineFunction;
import org.elasticsearch.common.lucene.search.function.FieldValueFactorFunction;
import org.elasticsearch.common.lucene.search.function.FunctionScoreQuery;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.functionscore.FieldValueFactorFunctionBuilder;
import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder;
import org.elasticsearch.index.query.functionscore.ScoreFunctionBuilder;
import org.elasticsearch.index.query.functionscore.ScoreFunctionBuilders;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * @author wxf
 * @date 2023/6/5 9:40
 */
@Service
@Slf4j
@RequiredArgsConstructor
public class SearchMysteryBoxServiceImpl implements SearchMysteryBoxService {

    private final ShopConfig shopConfig;

    private final LangManager langManager;

    @Override
    public EsPageVO<MicroreleaseProductSearchVo> microreleasePage(EsPageParam esPageParam, EsProductParam esProductParam, Boolean aFalse) {
        SearchResponse response = microreleasePageSearchResult(esPageParam, esProductParam, aFalse);
        return microreleaseSearchResult(esPageParam,response);
    }

    /**
     *  ES盲盒商品查询发送请求
     * @param pageParam
     * @param productParam
     * @param isAgg
     * @return
     */
    private SearchResponse microreleasePageSearchResult(EsPageParam pageParam, EsProductParam productParam, Boolean isAgg) {
        //检索请求组装
        SearchRequest searchRequest = buildMicroreleaseSearchRequest(pageParam, productParam, isAgg);
        return EsSearchUtil.search(searchRequest);
    }

    /**
     * ES盲盒商品检索请求组装
     *
     * @param pageParam
     * @param param
     * @param isAgg
     * @return
     */
    private SearchRequest buildMicroreleaseSearchRequest(EsPageParam pageParam,EsProductParam param, Boolean isAgg) {
        if (Objects.isNull(param.getAppDisplay())) {
            param.setAppDisplay(Boolean.FALSE);
        }
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        Integer lang = I18nMessage.getLang();
        // 指定返回数组中的字段
        if (ArrayUtil.isNotEmpty(param.getFetchSource())) {
            searchSourceBuilder.fetchSource(param.getFetchSource(), null);
        } else {
            searchSourceBuilder.fetchSource(EsConstant.BOX_APP_FETCH_SOURCE, null);
        }
        // 构建bool-query
        BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();

        recommendFilterQuery(param, boolQueryBuilder);
        // 排序
        param.setAppDisplay(Boolean.FALSE);
        sort(param, searchSourceBuilder, boolQueryBuilder, lang);
        //分页
        if (Objects.nonNull(pageParam)) {
            if (pageParam.getCurrent() <= 0) {
                pageParam.setCurrent(1);
            }
            searchSourceBuilder.from((pageParam.getCurrent()-1)*pageParam.getSize());
            searchSourceBuilder.size(pageParam.getSize());
        }
        // 进行聚合分析
        agg(param, searchSourceBuilder, isAgg);
        log.debug("构建的DSL语句 {}",searchSourceBuilder.toString());
        return new SearchRequest(new String[]{EsIndexEnum.MYSTERY_BOX.value()},searchSourceBuilder);
    }

    private void recommendFilterQuery(EsProductParam param, BoolQueryBuilder boolQueryBuilder) {
        // 直接上架商品信息
        BoolQueryBuilder upProd = new BoolQueryBuilder();
        List<Integer> prdoTypeStatus = new ArrayList<>();
        prdoTypeStatus.add(0);
        prdoTypeStatus.add(1);
        upProd.must(QueryBuilders.termsQuery(EsConstant.PROD_TYPE, prdoTypeStatus));
        ArrayList<Integer> upStatus = new ArrayList<>();
        upStatus.add(1); // 上架
        upStatus.add(4); // 售罄
        upStatus.add(9); // 预售
        upProd.must(QueryBuilders.termsQuery(EsConstant.STATUS, upStatus));
        upProd.must(QueryBuilders.termQuery(EsConstant.SOURCE, 0));
        if(Objects.nonNull(param.getShopId())){
            upProd.filter(QueryBuilders.termQuery(EsConstant.SHOP_ID, param.getShopId()));
        }
        boolQueryBuilder.should(upProd);
    }

    /**
     * 进行排序
     */
    private void sort(EsProductParam param, SearchSourceBuilder searchSourceBuilder, BoolQueryBuilder boolQueryBuilder, Integer lang) {
        // 用户端、商家端、平台端自定义排序
        if (Objects.nonNull(param.getSort())) {
            for (EsProductSortEnum enumValue : EsProductSortEnum.values()) {
                if (!Objects.equals(enumValue.value(), param.getSort())) {
                    continue;
                }
                searchSourceBuilder.sort(enumValue.param(), enumValue.order());
            }
            //封装所有的查询条件(没有function score)
            searchSourceBuilder.query(boolQueryBuilder);
            return;
        }

        // 1.关键字排序 -- 没有指定排序规则，且是关键字搜索的查询，统一按关键字优先排序（防止关键字搜素时，关键字的商品没有优先显示）
        // 2.用户端默认排序 -- 如果排序规则设为空，则按照一定的算分规则进行排序，否则按照用户指定排序规则进行排序()
        if (StrUtil.isNotBlank(param.getKeyword()) || param.getAppDisplay()) {
            keywordSort(param, searchSourceBuilder, boolQueryBuilder, lang);
            return;
        }
        if (Objects.equals(param.getShowSpuType(),1)) {

            FunctionScoreQueryBuilder functionScoreQueryBuilder = QueryBuilders.functionScoreQuery(boolQueryBuilder).boostMode(CombineFunction.SUM);
            searchSourceBuilder.query(functionScoreQueryBuilder);
            return;
        }

        // 商家、平台默认排序--商品序号 倒序， 创建时间 倒序
        if (Objects.nonNull(param.getShopId())) {
            // 商家端优先显示序号大的商品
            //searchSourceBuilder.sort(EsProductSortEnum.SEQ_DESC.param(), EsProductSortEnum.SEQ_DESC.order());
        } else {
            // 平台端优先置顶的商品, 再到销量（实际销量+注水销量）
            searchSourceBuilder.sort(EsProductSortEnum.IS_TOP_DESC.param(), EsProductSortEnum.IS_TOP_DESC.order());
            searchSourceBuilder.sort(EsProductSortEnum.SALE_NUM_DESC.param(), EsProductSortEnum.SALE_NUM_DESC.order());
        }
        searchSourceBuilder.sort(EsProductSortEnum.CREATE_TIME_DESC.param(), EsProductSortEnum.CREATE_TIME_DESC.order());
        searchSourceBuilder.sort(EsProductSortEnum.PROD_ID_DESC.param(), EsProductSortEnum.PROD_ID_DESC.order());
        searchSourceBuilder.query(boolQueryBuilder);
    }

    /**
     * 聚合分析
     */
    private void agg(EsProductParam param, SearchSourceBuilder searchSourceBuilder, Boolean isAgg) {
        // 店铺进行聚合
        if (param.getKeyword() != null && param.getKeyword().length() > 0) {
            searchSourceBuilder.aggregation(AggregationBuilders.terms(EsConstant.SHOP).field(EsConstant.SHOP_ID).size(1));
        }
        if (Objects.isNull(isAgg) || !isAgg) {
            return;
        }
        // 搜索平台商品，按照平台分类信息进行聚合
        if (Objects.isNull(param.getShopId()) && Objects.isNull(param.getCategoryId())) {
            searchSourceBuilder.aggregation(EsSearchUtil.nestedAggregation(EsConstant.CATEGORY, EsConstant.CATEGORY_UNION_ID, EsConstant.CATEGORY_ID, EsConstant.CATEGORY_INCLUDE));
        }
    }

    /**
     * 关键字搜索排序
     * @param param
     * @param searchSourceBuilder
     * @param boolQueryBuilder
     * @param lang
     */
    private void keywordSort(EsProductParam param, SearchSourceBuilder searchSourceBuilder, BoolQueryBuilder boolQueryBuilder, Integer lang) {
        List<FunctionScoreQueryBuilder.FilterFunctionBuilder> filterFunctionBuilders = new ArrayList<>();
        // 关键字搜索，优先匹配
        if (StrUtil.isNotBlank(param.getKeyword())) {
            FunctionScoreQueryBuilder.FilterFunctionBuilder spuName;
            // 权重调大，防止销量大的商品排在关键词商品的前面
            if (Objects.equals(lang, LanguageEnum.LANGUAGE_ZH_CN.getLang())) {
                spuName = new FunctionScoreQueryBuilder.FilterFunctionBuilder(QueryBuilders.termQuery(EsConstant.PROD_NAME_ZH, param.getKeyword()), ScoreFunctionBuilders.weightFactorFunction(200));
            } else {
                spuName = new FunctionScoreQueryBuilder.FilterFunctionBuilder(QueryBuilders.termQuery(EsConstant.PROD_NAME_EN, param.getKeyword()), ScoreFunctionBuilders.weightFactorFunction(200));
            }
            filterFunctionBuilders.add(spuName);
        }

        // 用户端默认排序优先使用是否置顶参数
        FunctionScoreQueryBuilder.FilterFunctionBuilder isTop = new FunctionScoreQueryBuilder.FilterFunctionBuilder(QueryBuilders.termQuery(EsConstant.IS_TOP, 50), ScoreFunctionBuilders.weightFactorFunction(1));
        filterFunctionBuilders.add(isTop);

        // 评论数 log1p
        ScoreFunctionBuilder<FieldValueFactorFunctionBuilder> commentNumScoreFunction = new FieldValueFactorFunctionBuilder(EsConstant.COMMENT_NUM).modifier(FieldValueFactorFunction.Modifier.LOG1P).factor(0.5f);
        filterFunctionBuilders.add(new FunctionScoreQueryBuilder.FilterFunctionBuilder(commentNumScoreFunction));
        // 销量数 log1p
        ScoreFunctionBuilder<FieldValueFactorFunctionBuilder> saleNumScoreFunction = new FieldValueFactorFunctionBuilder(EsConstant.SOLD_NUM).modifier(FieldValueFactorFunction.Modifier.LOG1P).factor(0.5f);
        filterFunctionBuilders.add(new FunctionScoreQueryBuilder.FilterFunctionBuilder(saleNumScoreFunction));

        filterFunctionBuilders.toArray();

        FunctionScoreQueryBuilder functionScoreQueryBuilder = QueryBuilders.functionScoreQuery(boolQueryBuilder, ArrayUtil.toArray(filterFunctionBuilders, FunctionScoreQueryBuilder.FilterFunctionBuilder.class))
                .scoreMode(FunctionScoreQuery.ScoreMode.SUM).boostMode(CombineFunction.SUM);
        // 封装所有的查询条件(带有function score)
        searchSourceBuilder.query(functionScoreQueryBuilder);
    }

    private EsPageVO<MicroreleaseProductSearchVo> microreleaseSearchResult(EsPageParam esPageParam, SearchResponse response) {
        EsPageVO<MicroreleaseProductSearchVo> esPageVO = new EsPageVO<>();
        // 1、返回的所有查询到的商品
        EsProductSearchVO productSearch = SearchResponseUtil.getProductSearch(response);
        getProdLang(productSearch);
        esPageVO.setRecords(BeanUtil.copyToList(productSearch.getProducts(),MicroreleaseProductSearchVo.class));
        SearchResponseUtil.buildSearchPage(esPageParam, esPageVO, response);
        return esPageVO;
    }

    private void getProdLang(EsProductSearchVO productSearch) {
        getProdLang(productSearch.getProducts());
        if (CollUtil.isEmpty(productSearch.getBrands()) && CollUtil.isEmpty(productSearch.getCategories())) {
            return;
        }
        LangConfig langConfig = shopConfig.getLang();
        Integer lang = I18nMessage.getLang();
        // 插入分类名称
        if (CollUtil.isNotEmpty(productSearch.getCategories())) {
            for (CategorySearchVO category : productSearch.getCategories()) {
                if (CollUtil.isEmpty(category.getCategoryLangList())) {
                    continue;
                }
                Map<Integer, CategoryLangBO> langMap = category.getCategoryLangList().stream()
                        .filter(categoryLang -> Objects.nonNull(categoryLang.getLang()) && Objects.nonNull(categoryLang.getCategoryName()))
                        .collect(Collectors.toMap(CategoryLangBO::getLang, c -> c));

                if(MapUtil.isEmpty(langMap)){
                    continue;
                }
                CategoryLangBO categoryLangBO = langMap.get(langManager.getLang(langMap.keySet(), langConfig, lang));
                category.setName(categoryLangBO.getCategoryName());
                category.setCategoryLangList(null);
            }
        }
        // 插入品牌名称
        if (CollUtil.isNotEmpty(productSearch.getBrands())) {
            for (BrandSearchVO brand : productSearch.getBrands()) {
                if (CollUtil.isEmpty(brand.getBrandLangList())) {
                    continue;
                }
                Map<Integer, BrandLangBO> langMap = brand.getBrandLangList().stream()
                        .filter(brandLang -> Objects.nonNull(brandLang.getLang()) && Objects.nonNull(brandLang.getName()))
                        .collect(Collectors.toMap(BrandLangBO::getLang, b -> b));

                if(MapUtil.isEmpty(langMap)){
                    continue;
                }
                BrandLangBO brandLangBO = langMap.get(langManager.getLang(langMap.keySet(), langConfig, lang));
                brand.setBrandName(brandLangBO.getName());
                brand.setBrandLangList(null);
            }
        }
    }

    private void getProdLang(List<ProductSearchVO> prodList) {
        LangConfig langConfig = shopConfig.getLang();
        Integer lang = I18nMessage.getLang();
        for (ProductSearchVO product : prodList) {
            if (CollUtil.isEmpty(product.getProdLangList())) {
                continue;
            }
            Map<Integer, ProductSearchLangVO> langMap = product.getProdLangList().stream().filter(prod -> Objects.nonNull(prod.getLang()) && Objects.nonNull(prod.getProdName())).collect(Collectors.toMap(ProductSearchLangVO::getLang, p -> p));
            if(langMap.size() == 0){
                product.setProdName(null);
                product.setBrief(null);
                continue;
            }
            ProductSearchLangVO productSearchLangVO = langMap.get(langManager.getLang(langMap.keySet(), langConfig, lang));

            product.setProdName(productSearchLangVO.getProdName());
            product.setBrief(productSearchLangVO.getBrief());
        }
    }
}
